<?php
/**
 * @version $Id: JClassDocumentor.php 28 2009-06-29 20:28:04Z der.el.kuku $
 * @package
 * @subpackage
 * @author		EasyJoomla {@link http://www.easy-joomla.org Easy-Joomla.org}
 * @author		Nikolai Plath (elkuku) {@link http://www.nik-it.de NiK-IT.de}
 * @author		Created on 09.02.2009
 *
 * Install:
 * Add
 * require_once( "$IP/extensions/PermCalc/PermCalc.php" );
 * to your LocaleSettings.php
 *
 * Usage:
 * <permcalc>Introtext(optional</permcalc>
 *
 * Required:
 * Have Fun =;)
 */

//--No direct access
defined('MEDIAWIKI') or die('=;)');

defined('DS') or define('DS', DIRECTORY_SEPARATOR);

$wgExtensionFunctions[] = "wfJClassDocumentor";
$wgExtensionCredits['parserhook'][] = array(
                'version'     => '1.0',
                'name'        => 'JClassDocumentor',
                'author'      => array('Nikolai Plath'),
                'email'       => 'nik@',
                'url'         => 'http://www.mediawiki.org/wiki/Extension:JClassDocumentor...soon',
                'description' => 'Interface for documenting Joomla! classes',
                );

function wfJClassDocumentor()
{
	global $wgParser, $wgHooks;
	$wgParser->setHook( "JClassDocumentor", "renderJClassDocumentor" );
	$wgHooks['OutputPageParserOutput'][] = 'fnJClassDocumentorOutputHook';
}//function

function renderJClassDocumentor($input, $argv, &$parser)
{
	global $wgScriptPath;
$b = dirname(__FILE__);
$baseDir = substr($b, 0, strpos($b, $wgScriptPath)+strlen($wgScriptPath));
$JVersion = '1.5.11';
$baseDir .= DS.'sources'.DS.'joomla'.DS.$JVersion.DS.'libraries'.DS.'joomla';
	$blub = '';$baseDir.'--blubber--'.$wgScriptPath;
	$baseFolders = EasyFolder::folders($baseDir);

	$html = '';
	$folder_name =( isset($_REQUEST['jdoc_folder']) ) ? $_REQUEST['jdoc_folder'] : '';
	$jdoc_file =( isset($_REQUEST['jdoc_file']) ) ? $_REQUEST['jdoc_file'] : '';

	if( $folder_name && ! in_array($folder_name, $baseFolders))
	{
		return 'Invalid folder';
	}
	if( $folder_name && EasyFolder::exists($baseDir.DS.$folder_name))
	{
		$classFiles = EasyFolder::files($baseDir.DS.$folder_name, '\.php$');//, true, true);
		$html .= 'aaa<pre>'.print_r($classFiles, true).'</pre>';
	}
	
	$html .= '<form name="jdocForm" action="'.$_SERVER['PHP_SELF'].'" method="GET">';
	$html .= '<select name="jdoc_folder" onchange="document.jdocForm.submit();">';
	$html .= '<option value="">Select...</option>';
	foreach ($baseFolders as $folder)
	{
		$selected =( $folder == $folder_name ) ? ' selected="selected"' : '';
#		$fName = str_replace($baseDir.DS.$folder_name, '', $folder);
		$html .= '<option value="'.$folder.'"'.$selected.'>'.$folder.'</option>';
	}//foreach
	
	$html .= '</select>';
	
	if( $folder_name )
	{
		$html .= '<select name="jdoc_file" onchange="document.jdocForm.submit();">';
		$html .= '<option value="">Select...</option>';
		foreach ($classFiles as $file)
		{
		$selected =( $file == $jdoc_file ) ? ' selected="selected"' : '';
			$html .= '<option value="'.$file.'"'.$selected.'>'.$file.'</option>';
		}//foreach
		
		$html .= '</select>';
	}

	$html .= '</form>';

	if( $jdoc_file && in_array($jdoc_file, $classFiles))
	{
		$html .= EasyReflector::reflect($baseDir.DS.$folder_name.DS.$jdoc_file);

//		$classFiles = EasyFolder::files($baseDir.DS.$folder_name, '\.php$');//, true, true);
//		$html .= 'aaa<pre>'.print_r($classFiles, true).'</pre>';
	}
	#	$html .= '<div onclick="document.jdocForm.submit();">kk</div>';
#	$html = '<div id="permcalc"><div style="font-weight: bold; background-color: red; padding: 10px;">Bitte aktivieren Sie Javascript in Ihrem Browser</div></div>';
#	$javascript  = '<!-- PermCalc Instance --><script type="text/javascript">drawForm();</script>';

	return $blub.$input.$html;//.$javascript;
}//function

/**
 * Entry point for the hook for printing JS and CSS:
 */
function fnJClassDocumentorOutputHook( &$m_pageObj, $m_parserOutput ) {
	global $wgScriptPath;

	//--CSS
	$m_pageObj->addLink(
    array(
      'rel'   => 'stylesheet',
      'type'  => 'text/css',
      'href'  => $wgScriptPath . '/extensions/PermCalc/JClassDocumentor.css'
    )
  );

  //--JS
#  $m_pageObj->addScriptFile($wgScriptPath.DS.'extensions'.DS.'PermCalc'.DS.'PermCalc.js');

  # Be nice:
  return true;
}//function

abstract class EasyFolder
{
	/**
	 * Wrapper for the standard file_exists function
	 *
	 * @param string $path Folder name relative to installation dir
	 * @return boolean True if path is a folder
	 * @since 1.5
	 */
	function exists($path)
	{
		return is_dir(self::clean($path));
	}

	/**
	 * Utility function to read the folders in a folder
	 *
	 * @param	string	$path		The path of the folder to read
	 * @param	string	$filter		A filter for folder names
	 * @param	mixed	$recurse	True to recursively search into sub-folders, or an integer to specify the maximum depth
	 * @param	boolean	$fullpath	True to return the full path to the folders
	 * @param	array	$exclude	Array with names of folders which should not be shown in the result
	 * @return	array	Folders in the given folder
	 * @since 1.5
	 */
	public function folders($path, $filter = '.', $recurse = false, $fullpath = false, $exclude = array('.svn', 'CVS'))
	{
		// Initialize variables
		$arr = array ();

		// Check to make sure the path valid and clean
		$path = self::clean($path);

		// Is the path a folder?
		if (!is_dir($path)) {
			echo 'JFolder::folder: Path is not a folder '.$path;
			return false;
		}

		// read the source directory
		$handle = opendir($path);
		while (($file = readdir($handle)) !== false)
		{
			$dir = $path.DS.$file;
			$isDir = is_dir($dir);
			if (($file != '.') && ($file != '..') && (!in_array($file, $exclude)) && $isDir) {
				// removes SVN directores from list
				if (preg_match("/$filter/", $file)) {
					if ($fullpath) {
						$arr[] = $dir;
					} else {
						$arr[] = $file;
					}
				}
				if ($recurse) {
					if (is_integer($recurse)) {
						$recurse--;
					}
					$arr2 = EasyFolder::folders($dir, $filter, $recurse, $fullpath);
					$arr = array_merge($arr, $arr2);
				}
			}
		}
		closedir($handle);

		asort($arr);
		return $arr;
	}
	/**
	 * Utility function to read the files in a folder
	 *
	 * @param	string	$path		The path of the folder to read
	 * @param	string	$filter		A filter for file names
	 * @param	mixed	$recurse	True to recursively search into sub-folders, or an integer to specify the maximum depth
	 * @param	boolean	$fullpath	True to return the full path to the file
	 * @param	array	$exclude	Array with names of files which should not be shown in the result
	 * @return	array	Files in the given folder
	 * @since 1.5
	 */
	function files($path, $filter = '.', $recurse = false, $fullpath = false, $exclude = array('.svn', 'CVS'))
	{
		// Initialize variables
		$arr = array ();

		// Check to make sure the path valid and clean
		$path = self::clean($path);

		// Is the path a folder?
		if (!is_dir($path)) {
			echo 'JFolder::files: Path is not a folder '.$path;
			return false;
		}

		// read the source directory
		$handle = opendir($path);
		while (($file = readdir($handle)) !== false)
		{
			$dir = $path.DS.$file;
			$isDir = is_dir($dir);
			if (($file != '.') && ($file != '..') && (!in_array($file, $exclude))) {
				if ($isDir) {
					if ($recurse) {
						if (is_integer($recurse)) {
							$recurse--;
						}
						$arr2 = EasyFolder::files($dir, $filter, $recurse, $fullpath);
						$arr = array_merge($arr, $arr2);
					}
				} else {
					if (preg_match("/$filter/", $file)) {
						if ($fullpath) {
							$arr[] = $path.DS.$file;
						} else {
							$arr[] = $file;
						}
					}
				}
			}
		}
		closedir($handle);

		asort($arr);
		return $arr;
	}
	/**
	 * Function to strip additional / or \ in a path name
	 *
	 * @static
	 * @param	string	$path	The path to clean
	 * @param	string	$ds		Directory separator (optional)
	 * @return	string	The cleaned path
	 * @since	1.5
	 */
	function clean($path, $ds=DS)
	{
		$path = trim($path);

		if (empty($path)) {
			$path = '';
		} else {
			// Remove double slashes and backslahses and convert all slashes and backslashes to DS
			$path = preg_replace('#[/\\\\]+#', $ds, $path);
		}

		return $path;
	}

	
}//class

class EasyReflector
{
/**
 * reflect a class
 *
 * @param string $path from JROOT
 * @param string $file filename
 */
function reflect($fullPath)
{
#	$fileName =  JPATH_ROOT.DS.$path.$file;
#	$fullPathFileName = JPATH_ROOT.DS.$path.$file;

	$html = '';
	defined('NL') or define('NL', "\n");
#	$ebc_project = JRequest::getVar('ebc_project', '');

#	JLoader::import('helpers.reflection', JPATH_COMPONENT);

#	$reflection = new easyReflection();

#	$reflection->printDeclaredClasses();

#	echo $path.'<br />'.$file.'<br />';

#		echo '<div class="explanation">'.$fullPath.'</div>';
		$allClasses = get_declared_classes();
#echo 'aha';
		define('JPATH_BASE', 1);
		/*
		 * WE INCLUDE A FILE !!
		 * TODO whatelse ??
		 */
		include_once $fullPath;
#echo 'aha';

		$foundClasses = array_diff(get_declared_classes(), $allClasses);

		if( ! count($foundClasses))
		{
			return 'No classes found';
		}
		else
		{
			foreach ($foundClasses as $clas)
			{
				$theClass = new ReflectionClass($clas);
				$parentName = $theClass->getParentClass();
				$parentName =( $parentName ) ? 'Extends: <span style="color: orange;">'.$parentName->name.'</span>':'';
				$html .= '<h1>';
				$html .= sprintf(
				   "%s%s%s %s" ,
				       $theClass->isInternal() ? 'internal' : 'user-defined',
				       $theClass->isAbstract() ? ' abstract' : '',
				       $theClass->isFinal() ? ' final' : '',
				       $theClass->isInterface() ? 'Interface' : 'Class'
				);
				$html .= ' <span style="color: green;">'.$clas.'</span> '.$parentName;
				$html .= '</h1>';
		#		echo NL.'<table width="100%"><tr valign="top"><td>';
				$html .= NL.'<h2>Properties</h2>';
		#		echo NL.'<pre>';
				$properties = $theClass->getProperties();
				foreach ($properties as $prop)
				{
					$property = $theClass->getProperty($prop->name);

					$html .= sprintf(
					   "%s%s%s%s property <strong>%s</strong>",
					       $property->isPublic() ? ' <strong style="color: green">public</strong>' : '',
					       $property->isPrivate() ? ' <strong style="color: orange">private</strong>' : '',
					       $property->isProtected() ? ' <strong style="color: red">protected</strong>' : '',
					       $property->isStatic() ? ' <strong style="color: black">static</strong>' : '',
					       $property->getName()

					);
#					echo '<br />';
				}//foreach
#				echo NL.'</pre>';

#				echo NL.'</td><td>';
#				echo NL.'<pre>'.$theClass->getDocComment().'</pre>';
#				echo NL.'</td></tr></table>';

				$html .= '<pre>'.$theClass->getDocComment().'</pre>';
				
				//				$constants = $theClass->getConstants();
//				print_r($constants);
				// Dokumentarischen Kommentar ausgeben
//printf("---> Dokumentation:\n %s\n", var_export($theClass->getDocComment(), 1));
//
//// Ausgeben, welche Interfaces von der Klasse implementiert werden
//printf("---> Implementiert:\n %s\n", var_export($theClass->getInterfaces(), 1));
//
//// Klassenkonstanten ausgeben
//printf("---> Konstanten: %s\n", var_export($theClass->getConstants(), 1));
//
//// Klasseneigenschaften ausgeben
//printf("---> Eigenschaften: %s\n", var_export($theClass->getProperties(), 1));
//
//// Klassenmethoden ausgeben
//printf("---> Methoden: %s\n", var_export($theClass->getMethods(), 1));echo '</pre>';
				#echo $theClass->getDocComment();
//				echo '<pre>';
//				printf(
//				   "===> %s%s%s %s '%s' [extends %s]\n" .
//				   "    deklariert in %s\n" .
//				   "    Zeilen %d bis %d\n" .
//				   "    hat die Modifizierer %d [%s]\n",
//				       $theClass->isInternal() ? 'internal' : 'user-defined',
//				       $theClass->isAbstract() ? ' abstract' : '',
//				       $theClass->isFinal() ? ' final' : '',
//				       $theClass->isInterface() ? 'interface' : 'class',
//				       $theClass->getName(),
//				       var_export($theClass->getParentClass(), 1),
//				       $theClass->getFileName(),
//				       $theClass->getStartLine(),
//				       $theClass->getEndline(),
//				       $theClass->getModifiers(),
//				       implode(' ', Reflection::getModifierNames($theClass->getModifiers()))
//				);

				$html .= NL.'<h2>Methods</h2>';

				$cMethods =  $theClass->getMethods();

#				$pane =& JPane::getInstance('sliders');
#				echo $pane->startPane("the-pane");
				$indent = 0;
				$displayClassName = '';
				foreach ($cMethods as $cMethod)
				{
					$mPath = $cMethod->getFileName();

					//--$this class or $that class ;)
					//--..base or extended
					//..also marks the extended extended classes orange.. TODO !
					#$color =( $mPath == $fullPathFileName ) ? 'green' : 'orange';
					$titel = sprintf(
					   "%s%s%s%s%s%s Method <strong style='color: orange;'>%s</strong>",
					       $cMethod->isAbstract()	? ' abstract' : '',
					       $cMethod->isFinal() 		? ' final' : '',
					       $cMethod->isPublic()		? ' <strong style="color: green">public</strong>' : '',
					       $cMethod->isPrivate()	? ' <strong style="color: orange">private</strong>' : '',
					       $cMethod->isProtected()	? ' <strong style="color: red">protected</strong>' : '',
					       $cMethod->isStatic()		? ' <strong style="color: black">static</strong>' : '',
					       $cMethod->getName()
					);
					$pClass= $cMethod->getDeclaringClass();
					$s = $pClass->getName();
					if( $s != $displayClassName )
					{

						$indent++;
						$html .= '<h1>';
					#	echo str_repeat("&nbsp;&nbsp;", $indent);
						$html .= ( $displayClassName ) ? '<span style="color: orange">Extends</span>&nbsp;'.$s : $s;
						$html .= '</h1>';
						$displayClassName = $s;

					}
					$paramString = array();
					$parameters = $cMethod->getParameters();
					foreach( $parameters as $parameter )
					{
						#$color =($parameter->isOptional() ) ? 'blue' : 'brown';
						$s = '';
						$s .= sprintf("%s<strong style='color: brown;'>$%s</strong>",
#					       $parameter->isOptional() ? '<strong style="color: blue;">optional</strong> ' : '',
					       $parameter->isPassedByReference() ? '<strong style="color: blue;"> & </strong>' : '',
					       $parameter->getName()
					   );

					   if( $parameter->isDefaultValueAvailable())
					   {
					   		$def = $parameter->getDefaultValue();
					   		if( $def === null)
					   		{
							   	$s .= '=NULL';
					   		}
					   		else if( $def === false )
					   		{
					   			$s .= '=FALSE';
					   		}
					   		else if( $def === true )
					   		{
					   			$s .= '=TRUE';
					   		}
					   		else if( $def === array() )
					   		{
					   			$s .= '=array()';
					   		}

					   		else if( $def === '' )
					   		{
					   			$s .= '=\'\'';
					   		}
					   		else
					   		{
					   			$s .= '='.$parameter->getDefaultValue();
					   		}
					   }
					   $paramString[] = $s;
					}//foreach
					$paramString = implode(', ', $paramString);
					$titel .= '( '.$paramString.' )';
				#	echo '<h3>'.$titel.'</h3>';
				#	$titel = '<span style="color: '.$color.'">'.$cMethod->name.'</span>';
		#				echo str_repeat("&nbsp;&nbsp;", $indent);
		#			echo $titel;
#					echo $pane->startPanel($titel, 'one-page');
					//--draw a link to the source code
					$link = 'index.php?option='.com_EASY_APP_ELKUKU_1.'&amp;ebc_project='.$ebc_project.'&amp;tmpl=component&amp;controller=stuffer&amp;task=display_snip';
					$link .= '&amp;file_path='.$mPath.'&amp;the_file='.$file;
					$link .= '&amp;start='.$cMethod->getStartLine().'&amp;end='.$cMethod->getEndLine();
					$comment = $cMethod->getDocComment();
					if( $comment )
					{
						$html .= NL.'<div style="border: 1px dotted grey; background-color: white;" class="doccomment_doccomment">';
					#	echo '<pre>';
					#	echo $comment;
						#$comment = str_replace("\n", '<br />', $comment);
						$html .= nl2br($comment);
						#echo $comment;
				#		echo '</pre>';
						$html .= '</div>';
					}
					$fileLocation = substr($cMethod->getFileName(), strlen(JPATH_SITE)).'<br />'.'Lines'.' # '.$cMethod->getStartLine().' - '.$cMethod->getEndLine();
//					echo NL.'<span class="doccomment_sourcecode hasTip" title="'.$fileLocation.'">';
//					echo NL.'<a class="modal" rel="{handler: \'iframe\', size: {x: 950, y: 550}, effects:Fx.Transitions.Bounce.easeOut}"    href="'.$link.'">';
//					echo 'Source code';
//					echo NL.'</a></span>';

		#			print_r($parameters);
#					echo var_export($cMethod->getDocComment(), 1);
		#			echo '<br />';
			#		echo NL.'<pre>';
					// Grundlegende Informationen ausgeben

					// Dokumentarischen Kommentar ausgeben
#					printf("---> Dokumentation:\n %s\n", var_export($cMethod->getDocComment(), 1));

					// Statische Variablen ausgeben, falls welche existieren
					if ($statics= $cMethod->getStaticVariables()) {
					   $html .= '<pre>'.sprintf("---> Statische Variablen: %s\n", var_export($statics, 1)).'</pre>';
					}
					#					echo '<hr />';
					#print_r ($cMethod);
#					echo NL.'</pre>';
					#					echo '<hr />';
#					echo $pane->endPanel();
				}//foreach
#					echo $pane->endPane();
			}//foreach
		}
		
		return $html;
}//function
	
}//class

